﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

using DimsMapper.Modules;
using Microsoft.DirectX.DirectDraw;

namespace DimsMapper
{
    public class FrmImage
    {
        internal class Frame
        {
            internal ushort width;
		    internal ushort height;
            internal int size;
            internal short x;
		    internal short y;
            internal byte[] frame;

            // выравнивающая ширина спрайта  
            internal int a_width;
            // Начальная позиция кадра по X/Y в текстуре спрайта
            internal int spriteOffX;
            internal int spriteOffY;

            internal Bitmap image;
	    }

	    ushort   FPS;
	    ushort   actionFrame;
	    ushort   numFrames; // количество кадров для одного направления
	    short[]  xCentreShift = new short[6];
	    short[]  yCentreShift = new short[6];
	    int   [] oriOffset    = new int[6]; // смещение для каждого направления
        Frame[,] frames; // кадры frm изображения

        int totalDir = 1;

        Surface sprite;

        public FrmImage(DBFile file)
        {
            file.SkipBytes(4);
            FPS = file.ReadShort();
            actionFrame = file.ReadShort();
            numFrames = file.ReadShort();

            for (int i = 0; i < 6; i++) {
                xCentreShift[i] = (short)file.ReadShort();
            }
            for (int i = 0; i < 6; i++) {
                yCentreShift[i] = (short)file.ReadShort();
            }
            for (int i = 0; i < 6; i++) {
                oriOffset[i] = file.ReadInt();
            }

            long frameAreaSize = file.ReadInt();
            if (frameAreaSize == 0) {
                frameAreaSize = file.Size() - file.Position();
            }

            byte[] frameData = new byte[frameAreaSize];
            frameData = file.ReadBytes((int)frameAreaSize);

            // Смотрим, сколько реальных направлений имеет FRM файл
            for (int i = 1; i < 6; i++) {
                if (oriOffset[i] != 0) totalDir++;
            }

            frames = new Frame[totalDir, numFrames];

            int widthSprite = 0;
            int heightSprite = 0;
            int maxHeight = 0;
            int maxWidth = 0;

            // Проходим цикл направлений
            for (int r = 0; r < totalDir; r++)
            {
                // Получаем смещение для данного направления
                int position = oriOffset[r];

                // Проходим цикл кадров
                for (int f = 0; f < numFrames; f++)
                {
                    frames[r, f] = new Frame();

                    ushort width = Utilites.GetShort(ref frameData, ref position);
                    ushort height = Utilites.GetShort(ref frameData, ref position);
                    int size = Utilites.GetInt(ref frameData, ref position);
                    frames[r, f].x = (short)Utilites.GetShort(ref frameData, ref position);
                    frames[r, f].y = (short)Utilites.GetShort(ref frameData, ref position);
                    frames[r, f].width = width;
                    frames[r, f].height = height;
                    frames[r, f].size = size;

                    frames[r, f].spriteOffX = widthSprite;
                    frames[r, f].spriteOffY = heightSprite;

                    // Получаем кол-во байт, необходимых для кратности scanline
                    int a_width = ((4 - width % 4) % 4) + width;
                    frames[r, f].a_width = a_width;

                    // Получаем размер буффера для преобразованного растра
                    int a_size = a_width * height;
                    frames[r, f].frame = new byte[a_size];

                    int sPos = position;
                    int dPos = 0;

                    for (int h = 0; h < height; h++)
                    {
                        Buffer.BlockCopy(frameData, sPos, frames[r, f].frame, dPos, width);
                        dPos += a_width;
                        sPos += width;
                    }
                    // Увеличиваем смещение на длинну кадра для обработки следующего
                    position += size;

                    widthSprite += width;
                    maxHeight = Math.Max(height, maxHeight);
                }
                // Складываем высоты для каждого направления
                heightSprite += maxHeight;

                // Находим максимальную длинну суммы кадров направлений
                maxWidth = Math.Max(widthSprite, maxWidth);

                widthSprite = 0;
                maxHeight = 0;
            }
            CreateSprite(maxWidth, heightSprite);
        }

        void CreateSprite(int totalW, int totalH)
        {
            sprite = MapDraw.CreateSpriteSurface(totalW, totalH);

            var dc = sprite.GetDc();

            // DIB_RGB_COLORS = 0
            // SRCCOPY = 0xCC0020

            for (int r = 0; r < totalDir; r++)
            {
                for (int f = 0; f < numFrames; f++)
                {
                    FrmFiles.FrmPal.bmi.bmiHeader.biWidth = frames[r, f].width;
                    FrmFiles.FrmPal.bmi.bmiHeader.biHeight = -frames[r, f].height;

                    int result = GDI.StretchDIBits(dc, frames[r, f].spriteOffX, frames[r, f].spriteOffY, frames[r, f].width, frames[r, f].height,
                                                   0, 0, frames[r, f].width, frames[r, f].height, frames[r, f].frame, ref FrmFiles.FrmPal.bmi, 0, 0xCC0020);
                    if (result != frames[r, f].height) {
                        return; // ошибка
                    }
                }
            }
            sprite.ReleaseDc(dc);
        }

        public Bitmap GetImage(int dir, int frame)
        {
            Bitmap image = frames[dir, frame].image;
            if (image != null) return image;
             
            int w = frames[dir, frame].a_width;
            int h = frames[dir, frame].height;

            image = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            image.Palette = FrmFiles.FrmPal.palette;

            BitmapData data = image.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, image.PixelFormat);
            Marshal.Copy(frames[dir, frame].frame, 0, data.Scan0, frames[dir, frame].frame.Length);
            image.UnlockBits(data);

            frames[dir, frame].image = image;
            return image;
        }

        public Surface GetSprite()
        {
            return sprite;
        }

        public int GetWidth(int dir, int frame)
        {
            if (dir >= totalDir) dir = 0;
            return frames[dir, frame].width;
        }

        public int GetHeight(int dir, int frame)
        {
            if (dir >= totalDir) dir = 0;
            return frames[dir, frame].height;
        }

        public int TotalDirections()
        {
            return totalDir;
        }

        public int SpriteX(int dir, int frame)
        {
            if (dir >= totalDir) dir = 0;
            return frames[dir, frame].spriteOffX;
        }

        public int SpriteY(int dir, int frame)
        {
            if (dir >= totalDir) dir = 0;
            return frames[dir, frame].spriteOffY;
        }

        public int GetXOffDir(int dir)
        {
            return xCentreShift[dir];
        }

        public int GetYOffDir(int dir)
        {
            return yCentreShift[dir];
        }

        public byte GetPixel(int X, int Y, int dir, int frame)
        {
            if (dir >= totalDir) dir = 0;

            Frame dataFrame = frames[dir, frame];
            if (dataFrame != null) {
                //Y--; //Y = dataFrame.height - Y;
                return dataFrame.frame[dataFrame.a_width * Y + X];
            }
            return 0;
        }

        public byte GetCenterPixelColor()
        {
            Frame data = frames[0, 0];
            int x = data.width >> 1;
            int y = data.height >> 1;

            byte pixel = data.frame[x + (y * data.a_width)];
            return pixel;
        }

        public Color GetPixelColor(int x, int y)
        {
            Frame data = frames[0, 0];
            byte pixel = data.frame[x + (y * data.a_width)];

            return FrmFiles.FrmPal.GetColor(pixel);
        }
    }
}
